using System;
using System.Collections.Generic;
using System.Text;

using Mono.Cecil;
using Mono.Cecil.Cil;

namespace CILDiffLibPrime.Attributes
{
    class CecilFriendlyCustomAttributes
    {
        //AssemblyDefinition sourceAssembly;
        public CecilFriendlyCustomAttributes(string attributeAssemblyLocation)
        {
            //sourceAssembly = AssemblyFactory.GetAssembly(attributeAssemblyLocation);
        }

        private void AddAttributeParameters(MethodDefinition ctor, AssemblyDefinition ad)
        {
            ParameterDefinition pd = new ParameterDefinition("Difference",
                                                              0,
                                                              ParameterAttributes.None,
                                                              ad.MainModule.Import(typeof(Int32)));
            ctor.Parameters.Add(pd);

            ParameterDefinition pd2 = new ParameterDefinition("BodyDifferences",
                                                              0,
                                                              ParameterAttributes.None,
                                                              ad.MainModule.Import(typeof(String)));
            ctor.Parameters.Add(pd2);
        }

        private void ConstructBody(MethodDefinition ctor, AssemblyDefinition ad)
        {
            CilWorker cil = ctor.Body.CilWorker;

            MethodReference mr = ad.MainModule.Import(typeof(Attribute).GetConstructor
            (System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance, null,
            Type.EmptyTypes, null));

            cil.Emit(OpCodes.Ldarg_0);
            cil.Emit(OpCodes.Call, mr);
            cil.Emit(OpCodes.Ret);
        }

        MethodReference _methodType = null;

        private MethodReference CreateMethodAttributeType(AssemblyDefinition ad)
        {
            if (_methodType != null)
                return _methodType;

            TypeReference attributeType = ad.MainModule.Import(typeof(Attribute));
            TypeDefinition customType = new TypeDefinition("CILDiff_MethodDifferenceAttribute",
            string.Empty, TypeAttributes.Public, attributeType);

            MethodDefinition ctor = new MethodDefinition(MethodDefinition.Ctor, MethodAttributes.Public, ad.MainModule.Import(typeof(void)));
            AddAttributeParameters(ctor, ad);
            ConstructBody(ctor, ad);

            customType.Constructors.Add(ctor);
            
            //ad.MainModule.Inject(customType);
            _methodType =  ctor;

            return _methodType;
        }

        public void SetMethodAttribute( AssemblyDefinition ad, MethodDefinition targetMd, params object[] values )
        {
            System.Reflection.ConstructorInfo ci = GetConstructor(typeof(CILDiff_MethodDifferenceAttribute));
            SetAttribute(ad, targetMd, ci, values);
        }

        public void SetFieldAttribute(AssemblyDefinition ad, MethodDefinition targetMd, params object[] values)
        {
            System.Reflection.ConstructorInfo ci = GetConstructor(typeof(CILDiff_FieldDifferenceAttribute));
            SetAttribute(ad, targetMd, ci, values);
        }

        public void SetPropertyAttribute(AssemblyDefinition ad, MethodDefinition targetMd, params object[] values)
        {
            System.Reflection.ConstructorInfo ci = GetConstructor(typeof(CILDiff_PropertyDifferenceAttribute));
            
            SetAttribute(ad, targetMd, ci, values);
        }

        internal void SetClassAttribute(AssemblyDefinition ad, TypeDefinition targetTd, params object[] values)
        {
            System.Reflection.ConstructorInfo ci = GetConstructor(typeof(CILDiff_ClassDifferenceAttribute));
            SetAttribute(ad, targetTd, ci, values);
        }
        [CILDiff_FieldDifference(0, "", 0, "", 0, "", 0, "")]
        int m_foosdf;

        internal void SetAttribute(AssemblyDefinition ad, Mono.Cecil.ICustomAttributeProvider target,
            System.Reflection.ConstructorInfo ci, ICollection<object> values)
        {
            ad.MainModule.Import(typeof(SectionChange));
            MethodReference type = ad.MainModule.Import(ci);
            
            CustomAttribute attrib = new CustomAttribute(type);
            attrib.Constructor = type;

            foreach (SectionChange v in values)
            {
                //attrib.ConstructorParameters.Add(v);
                attrib.ConstructorParameters.Add(v.DifferenceType);
                attrib.ConstructorParameters.Add(v.Difference);
            }
            ad.MainModule.Import(type);
            target.CustomAttributes.Add(attrib);
        }

        private System.Reflection.ConstructorInfo GetConstructor( Type t )
        {
            foreach (System.Reflection.ConstructorInfo ci in t.GetConstructors())
            {
                if (ci.GetParameters().Length > 0)
                    return ci;
            }
            return null;
        }
    }
}
